package com.cwnu.sw.cys.core.common.intecepter;

import com.cwnu.sw.cys.core.common.constant.MDCKey;
import com.cwnu.sw.cys.core.common.exception.HttpErrorEnum;
import com.cwnu.sw.cys.core.user.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.Optional;

/**
 * Token拦截器，用于处理请求的Token验证、登录态维护及日志上下文管理
 * @author Administrator
 */
@Order(-2)
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {

    /**
     * HTTP头中Token的键名
     */
    public static final String AUTHORIZATION_HEADER = "Authorization";

    /**
     * Token的认证模式前缀（如 Bearer）
     */
    public static final String AUTHORIZATION_SCHEMA = "Bearer ";

    /**
     * 请求中存储用户ID的属性键
     */
    public static final String ATTRIBUTE_UID = "uid";


    @Autowired
    private LoginService loginService;

    /**
     * 处理请求前的拦截逻辑
     * @param request  当前请求对象
     * @param response 当前响应对象
     * @param handler  处理请求的处理器对象
     * @return 是否允许继续处理请求（返回false时中断请求链）
     */
    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
//        long startTime = System.currentTimeMillis();
//        request.setAttribute("startTime", startTime);

        // 获取请求中的Token
        String token = getToken(request);

        // 验证Token有效性获取用户ID
        Long validUid = loginService.getValidUid(token);
        if (Objects.nonNull(validUid)) {
            // 存在有效登录态
            request.setAttribute(ATTRIBUTE_UID, validUid);
        } else {
            // 判断是否为无需登录的公共路径
            boolean isPublicUri = isPublicUri(request.getRequestURI());
            if (!isPublicUri) {
                // 非公共路径且未登录时拒绝访问
                HttpErrorEnum.ACCESS_DENIED.sendHttpError(response);
                return false;
            }
        }

        // 将用户ID写入MDC日志上下文
        MDC.put(MDCKey.UID, String.valueOf(validUid));
        return true;
    }

    /**
     * 请求处理完成后的清理操作
     * @param request  当前请求对象
     * @param response 当前响应对象
     * @param handler  处理请求的处理器对象
     * @param ex       处理过程中抛出的异常（可能为null）
     */
    @Override
    public void afterCompletion(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
//        long startTime = (Long) request.getAttribute("startTime");
//        long duration = System.currentTimeMillis() - startTime;
//        log.info("请求耗时：{}ms", duration);

        // 清理MDC上下文中的用户ID
        MDC.remove(MDCKey.UID);
    }

    /**
     * 判断请求URI是否为无需登录的公共路径
     * @param requestUri 需要验证的请求路径
     * @return true表示是公共路径，false表示需要登录
     */
    private boolean isPublicUri(String requestUri) {
        String[] split = requestUri.split("/");
        return split.length > 3 && "public".equals(split[3]);
    }

    /**
     * 从HTTP头中解析Bearer Token
     * @param request 当前请求对象
     * @return 解析后的Token字符串（可能为null）
     */
    private String getToken(HttpServletRequest request) {
        String header = request.getHeader(AUTHORIZATION_HEADER);
        return Optional.ofNullable(header)
                .filter(h -> h.startsWith(AUTHORIZATION_SCHEMA))
                .map(h -> h.substring(AUTHORIZATION_SCHEMA.length()))
                .orElse(null);
    }
}
